public class MarsCalc {
	
	private double gnomonDeg = 0.0;
	
	private int hour;
	private int minute;
	private int day;
	private int year;
	private int month;
	private String time;
	private double elevenOne;
	private double tenTwo;
	private double nineThree;
	private double eightFour;
	private double sevenFive;
	private String AMPM;
	private String minuteStr;
	private double testN;
	private double testJD;
	private double testPBS;
	private double testFMS;
	private double testOffset;
	private double testM;
	private double testLs;
	private double testVminM;
	
	public MarsCalc(){
	}
	
	/**
	 * Calculates the location of the given longitude and latitude.
	 * 
	 * @param longDeg - just the degrees of the longitude entered by the user. 
	 * @param longMin - just the minutes of the longitude entered by the user.
	 * @param latDeg  - just the degrees of the latitude entered by the user.
	 * @param latMin  - just the minutes of the latitude entered by the user.
	 * @param month   - the month entered by the user.
	 * @param day	  - the day entered by the user.
	 * @param year	  - the year entered by the user.
	 */
	public void location(double longDeg, double latDeg, int year, int month, int day){
		boolean validDate;
		boolean validLongLat;
		validDate = isValidDate(day);
		validLongLat = isValidLongLat(longDeg, latDeg);
		
		if(validDate && validLongLat){
			
			this.day = day;
			this.year = year;
			this.month = month;
			this.setGnomon(latDeg);
			this.setDay(day);
			this.setTime(longDeg);
			this.setAngles(latDeg);
			
		
		}else{
			if(!validDate){
				System.out.println("The date entered is not a valid date.");
			}
			if(!validLongLat){
				System.out.println("The longitude and latitude entered is not valid.");
			}
			System.out.println("Please Try Again.");
		}
	}
	
	/**
	 * Gets the time at noon.
	 * 
	 * @return - The time to put at the top of the drawing.
	 */
	public String getNoon(){
		return time;
	}
	
	/**
	 * Found calculations on www.giss.nasa.gov
	 * 
	 * @param longDeg
	 */
	public void setTime(double longDeg){
		
		double N;
		double JD = 367 * this.year - (int)(7 * (this.year + (int)((this.month + 9) / 12)) / 4);
		JD -= (int)(3 * (int) ((this.year + (this.month - 9) / 7) / 100) + 1) / 4;
		JD += (int)(275 * this.month / 9) + this.day + 1721028.5;
		N = (JD - 1) - 2451545;
		
		double PBS = .0071 * Math.cos(this.degreeToRad(.985626 * N / 2.2353 + 49.409));
		PBS += .0057 * Math.cos(this.degreeToRad(.985626 * N / 2.7543 + 168.173));
		PBS += .0039 * Math.cos(this.degreeToRad(.985626 * N / 1.1177 + 191.837));
		PBS += .0037 * Math.cos(this.degreeToRad(.985626 * N / 15.7866 + 21.736));
		PBS += .0021 * Math.cos(this.degreeToRad(.985626 * N / 2.1354 + 15.704));
		PBS += .0020 * Math.cos(this.degreeToRad(.985626 * N / 2.4694 + 95.528));
		PBS += .0018 * Math.cos(this.degreeToRad(.985626 * N / 32.8493 + 49.095));
		
		//Fictitious mean sun
		double fms = 270.3863 + .5240384 * N;
		
		//Sets fms if it is above 360
		if(fms > 360){
			fms = fms - ((int)(fms / 360)) * 360;
		}
		System.out.println(fms);
		double M = 19.387 + .52402075 * N;
		
		//Sets M if it is above 360
		if(M > 360){
			M = M - ((int)(M / 360)) * 360;
		}
		System.out.println(M);
		double VminM = (10.691 + .0000003 * N) * Math.sin(this.degreeToRad(M)) + .623 * Math.sin(this.degreeToRad(2 * M)) + .05 * Math.sin(this.degreeToRad(3 * M)) + .005 * Math.sin(this.degreeToRad(5 * M)) + PBS;
		double Ls = fms + VminM;
		
		//Sets Ls is it is above 360
		if(Ls > 360){
			Ls = Ls - ((int)(Ls / 360)) * 360;
		}
		System.out.println(Ls);
		double offset = 2.861 * Math.sin(this.degreeToRad(2 * Ls)) - .071 * Math.sin(this.degreeToRad(4 * Ls)) + .002 * Math.sin(this.degreeToRad(6 * Ls)) - VminM;
		
		this.testM = M;
		this.testVminM = VminM;
		this.testLs = Ls;
		this.testOffset = offset;
		
		offset = offset / 15 * 60;
		
		//Sets the offset to the nearest minute
		if(offset - (int)offset > .49){
			offset = (int)offset + 1;
		}else{
			offset = (int)offset;
		}
		
		if(offset < 0){
			this.minute = (int)((longDeg % 15) * 4 + offset);
      this.hour = 11;
		}else{
			this.minute = (int)((longDeg % 15) * 4 + offset);
      this.hour = 12;
		}
		
		if(minute < 10 && minute > -10){
			minuteStr = "0" + Math.abs(minute);
		}else{
			minuteStr = "" + Math.abs(minute);
		}	
		
		this.testFMS = fms;
		this.testN = N;
		this.testJD = JD;
		this.testPBS = PBS;
      
    this.time = this.hour + ":" + minuteStr;
		
		if(this.hour == 11){
			this.setAMPM("5:" + minuteStr);
		}else if(this.hour == 12){
			this.setAMPM("6:" + minuteStr);
		}else{
			this.setAMPM("7:" + minuteStr);
		}
	}
	
	/**
	 * Gets the AMPM time.
	 * 
	 * @return - The AMPM times.
	 */
	public String getAMPM(){
		return AMPM;
	}

	/**
	 * Sets the AMPM times.
	 * 
	 * @param AMPM - The time for AMPM
	 */
	private void setAMPM(String AMPM){
		this.AMPM = AMPM;
	}
	
	/**
	 * Sets the angle of the lines.
	 * 
	 * @param latDeg - The degree the user entered for the latitude.
	 */
	private void setAngles(double latDeg){
		//Calculates the angle of the time lines.
		this.elevenOne	= this.radianToDeg(Math.atan(Math.tan(degreeToRad(15)) * Math.sin(degreeToRad(latDeg))));
		this.tenTwo 	= this.radianToDeg(Math.atan(Math.tan(degreeToRad(30)) * Math.sin(degreeToRad(latDeg))));
		this.nineThree 	= this.radianToDeg(Math.atan(Math.tan(degreeToRad(45)) * Math.sin(degreeToRad(latDeg))));
		this.eightFour 	= this.radianToDeg(Math.atan(Math.tan(degreeToRad(60)) * Math.sin(degreeToRad(latDeg))));
		this.sevenFive 	= this.radianToDeg(Math.atan(Math.tan(degreeToRad(75)) * Math.sin(degreeToRad(latDeg))));
	}
	
	/**
	 * Gets the angle of the lines for seven and five.
	 * 
	 * @return - The angle the line is supposed to be for seven and four.
	 */
	public double getSevenFive(){
		return sevenFive;
	}
	
	/**
	 * Gets the angle of the lines for eight and four.
	 * 
	 * @return - The angle the line is supposed to be for eight and four.
	 */
	public double getEightFour(){
		return eightFour;
	}
	
	/**
	 * Gets the angle of the lines for ten and two.
	 * 
	 * @return - The angle the line is supposed to be for nine and three.
	 */
	public double getTenTwo(){
		return tenTwo;
	}
	
	/**
	 * Gets the angle of the lines for nine and three.
	 * 
	 * @return - The angle the line is supposed to be for nine and three.
	 */
	public double getNineThree(){
		return nineThree;
	}
	
	/**
	 * Gets the angle of the lines for eleven and one.
	 * 
	 * @return - The angle the line is supposed to be for eleven and one.
	 */
	public double getElevenOne(){
		return elevenOne;
	}
	
	/**
	 * Gets the gnomon degree.
	 * 
	 * @return - The angle of the gnomon.
	 */
	public double getGnomon(){
		return gnomonDeg;
	}
	
	/**
	 * Sets the degree of the gnomon.
	 * 
	 * @param gnomon - The latitude the user entered.
	 */
	private void setGnomon(double gnomon){
		this.gnomonDeg = Math.abs(gnomon);
	}
	
	/**
	 * Gives the day of the year calculated by setDay.
	 * 
	 * @return The day of the year.
	 */
	private int getDay(){
		return this.day;
	}
	
	/**
	 * Calculates the day of the year and sets it to a 1-365 scale.
	 * 
	 * @param month	- The month the user entered.
	 * @param day	- The day of the year the user entered.
	 */
	private void setDay(int day){
		this.day = day;
	}
	
	/**
	 * Converts the radian value to degrees.
	 * 
	 * @param rad - The radian to turn into degrees.
	 * 
	 * @return The degree value of the radian.
	 */
	private double radianToDeg(double rad){
		return(rad * 180 / Math.PI);
	}
	
	/**
	 * Converts the degree value to radians.
	 * 
	 * @param deg - The degree to turn into radians.
	 * 
	 * @return The radian value of the degree.
	 */
	private double degreeToRad(double deg){
		return(deg * Math.PI / 180);
	}
	
	/**
	 * Checks to make sure the user entered a valid date.
	 * 
	 * @param 	month - the month of the year.
	 * @param 	day   - the day of the year.
	 * @param 	year  - the year.
	 * 
	 * @return 	true if the date is valid.
	 * 			false if the date is invalid.
	 */
	private boolean isValidDate(int day){
		
		if(day > 0 && day < 669){
			return true;
		}
		
		return false;
	}
	
	/**
	 * Checks the longitude and latitude to make sure they're valid values.
	 * 
	 * @param longDeg	- The value the user entered for longitude.
	 * @param latDeg	- The value the user entered for latitude.
	 * @return - true if the date is valid.
	 * 			 false if the date is invalid.
	 */
	private boolean isValidLongLat(double longDeg, double latDeg){
		
		if(longDeg > 180 || longDeg < -180){
			return false;
		}
		
		if(latDeg > 90 && latDeg < -90){
			return false;
		}
		
		return true;
	}
	
	//Tests offset
	public double testOffset(){
		return this.testOffset;
	}
	
	//Tests PBS
	public double testPBS(){
		return this.testPBS;
	}
	
	//Tests FMS
	public double testFMS(){
		return this.testFMS;
	}
	
	//Tests N
	public double testN(){
		return this.testN;
	}
	
	//Tests JD
	public double testJD(){
		return this.testJD;
	}
	
	//Tests VminM
	public double testVminM(){
		return this.testVminM;
	}
	
	//Tests Ls
	public double testLs(){
		return this.testLs;
	}
	
	//Tests M
	public double testM(){
		return this.testM;
	}
}
